#include <Arduino.h>
#include "WiFi.h"
#include "PubSubClient.h"
#include "Ticker.h"
//#include "uFire_SHT20.h"
#include "ArduinoJson.h"
 #include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>


//uFire_SHT20 sht20;
 
const char* ssid = "OnePlus12";              //wifi名
const char* password = "789852465";      //wifi密码
const char* mqtt_server = "mqtts.heclouds.com"; //onenet 的 IP地址 mqtts.heclouds.com 183.230.40.96
const int port = 1883;                    //端口号

#define mqtt_user "5RRfWDSPQr"    //产品ID
#define TM_DEVICE_NAME "lora_0"       //设备名称
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F5RRfWDSPQr%2Fdevices%2Flora_0&et=1742554048&method=md5&sign=CJh581XeSEC4PWuXawFNYA%3D%3D"       //鉴权信息

int retry_count=0;

WiFiClient espClient;           //创建一个WIFI连接客户端
PubSubClient client(espClient); // 创建一个PubSub客户端, 传入创建的WIFI客户端

Ticker tim1;                    //定时器,用来循环上传数据
Ticker tim2;                    //定时器,用来循环上传数据
int heart=70;
float ti_wen=37;
int shuai_dao=0;
static JsonDocument doc;

static JsonDocument Doc1;

static JsonDocument doc2;

//设备上传数据的post主题
#define ONENET_TOPIC_PROP_POST "$sys/5RRfWDSPQr/lora_0/thing/property/post"
//接受上传数据成功的回复主题
#define ONENET_TOPIC_PROP_POST_REPLY "$sys/5RRfWDSPQr/lora_0/thing/property/post/reply"
//接收下发属性设置主题
#define ONENET_TOPIC_PROP_SET  "$sys/5RRfWDSPQr/lora_0/thing/sub/property/get"
//接收下发属性设置成功的回复主题
#define ONENET_TOPIC_PROP_SET_REPLY "$sys/5RRfWDSPQr/lora_0/thing/sub/property/get_reply"
 
//接收设备属性获取命令主题
#define ONENET_TOPIC_PROP_GET "$sys/5RRfWDSPQr/lora_0/thing/property/get"
//接收设备属性获取命令成功的回复主题
#define ONENET_TOPIC_PROP_GET_REPLY "$sys/5RRfWDSPQr/lora_0/thing/property/get_reply"

//接受设备获取子设备属性命令主题
#define ONENET_TOPIC_PROP_GET_SUB "$sys/5RRfWDSPQr/lora_0/thing/sub/property/get"
//接受设备获取子设备属性命令成功的回复主题
#define ONENET_TOPIC_PROP_GET_SUB_REPLY "$sys/5RRfWDSPQr/lora_0/thing/sub/property/get_reply"

 
//这是post上传数据使用的模板
#define ONENET_POST_BODY_FORMAT "{\"id\":%d,\"dp\":%s}"
//#define ONENET_POST_BODY_FORMAT
int postMsgId = 0; //记录已经post了多少条

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

#define OLED_RESET    -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // I2C address of the OLED display

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void oled_init() {
  // 初始化 OLED 显示屏
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }
  // 清空显示屏缓冲区
  display.clearDisplay();
}
// 画旋转的等边三角形
void drawRotatingTriangle(int angle) {
  // 等边三角形的三个顶点
  int x0 = 64 + 20 * cos(angle * PI / 180);
  int y0 = 32 + 20 * sin(angle * PI / 180);
  int x1 = 64 + 20 * cos((angle + 120) * PI / 180);
  int y1 = 32 + 20 * sin((angle + 120) * PI / 180);
  int x2 = 64 + 20 * cos((angle + 240) * PI / 180);
  int y2 = 32 + 20 * sin((angle + 240) * PI / 180);

  // 画三角形
  display.drawLine(x0, y0, x1, y1, SSD1306_WHITE);
  display.drawLine(x1, y1, x2, y2, SSD1306_WHITE);
  display.drawLine(x2, y2, x0, y0, SSD1306_WHITE);
}

void showStartupAnimation() {
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  
  for (int i = 0; i < 360; i += 10) {
    display.clearDisplay();
    drawRotatingTriangle(i);
    display.display();
    delay(50); // 控制动画速度
  }
  
  // 清空显示屏
  display.clearDisplay();
  display.display();
}



void showWifiConected()
{
   // 显示WiFi连接动画
  for (int i = 0; i < 5; i++) { // 重复5次以增加动画效果
    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Connecting to WiFi...");
    display.display();
    delay(500);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Connecting to WiFi.");
    display.display();
    delay(500);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Connecting to WiFi..");
    display.display();
    delay(500);

    display.clearDisplay();
    display.setCursor(0, 0);
    display.println("Connecting to WiFi...");
    display.display();
    delay(500);
  }
}

void init_publishTopic() {
   //设备属性上报
  Doc1["post"] = "$sys/5RRfWDSPQr/lora_0/thing/pack/post";
  //网关子设备属性上报
  //设备事件上报
  Doc1["event_post"] = "$sys/5RRfWDSPQr/lora_0/thing/event/post";
  //网关子设备事件上报
  Doc1["sub_event_post"] = "$sys/5RRfWDSPQr/lora_0/thing/pack/post";
  //直连设备上下线
  Doc1["status_post"] = "/device/online";
} 
//连接WIFI相关函数
void setup_wifi() {
  
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  randomSeed(micros());

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}


//向主题发送模拟的温湿度数据
// void sendswitch_light_colortempAndBody_temperature()
// {
//   if (client.connected())
//   {
//     //先拼接出json字符串
//     char param[82];
//     char jsonBuf[178];
//     //sprintf(param, "{ \"switch_light_colortemp\":[{\"v\":%d}],\"Body_temperature\":[{\"v\":%.2f}] }", switch_light_colortemp, Body_temperature); //我们把要上传的数据写在param里
//     sprintf(param, "{ \"switch_light_colortemp\":[{\"v\":%d}] }", switch_light_colortemp); //我们把要上传的数据写在param里
//     postMsgId += 1;
//     sprintf(jsonBuf, ONENET_POST_BODY_FORMAT, postMsgId, param);
//     //再从mqtt客户端中发布post消息
//     if (client.publish(ONENET_TOPIC_PROP_POST, jsonBuf))
//     {
//       Serial.print("Post message to cloud: ");
//       Serial.println(jsonBuf);
//     }
//     else
//     {
//       Serial.println("Publish message to cloud failed!");
//     }
//   }

// }


//重连函数, 如果客户端断线,可以通过此函数重连
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(TM_DEVICE_NAME,mqtt_user,mqtt_password)){
      Serial.println("MQTT connected");
      // Once connected, publish an announcement...
      retry_count=0;
    }
    else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
      //增加重试次数3次，连接失败后采取其他措施（如重启设备）。
      retry_count++;
      if (retry_count >= 8) {
        // 重试次数达到8次，执行其他操作，如重启设备
      ESP.restart(); // 添加这行代码以重启ESP32
    }
    }

}

}

void callback(char *topic, byte *payload, unsigned int length)
{
  Serial.println("-------new message from server-----");
  Serial.print("topic:");
  Serial.println(topic);
  for (size_t i = 0; i < length; i++)
  {
    Serial.print((char)payload[i]);
  }
  Serial.println();
  if (strstr(topic, ONENET_TOPIC_PROP_SET))
  {
    
    DeserializationError error = deserializeJson(doc, payload);
    if (error)
    {
      Serial.println("parse json failed");
      return;
    }
    JsonObject setAlinkMsgObj = doc.as<JsonObject>();
    serializeJsonPretty(setAlinkMsgObj, Serial);
    String str = setAlinkMsgObj["id"];
    Serial.println(str);
    char sendbuf[120];
     sprintf(sendbuf, "{\"id\":\"%s\",\"code\":200,\"msg\":\"succ\",\"data\":{\"ti_wen\":%.1f,\"heart\":%d,\"shuai_dao\":%d}}", str.c_str(), ti_wen, heart, shuai_dao);
    Serial.println(sendbuf);
    client.publish(ONENET_TOPIC_PROP_SET_REPLY, sendbuf);
    Serial.println(sendbuf);
    client.publish(ONENET_TOPIC_PROP_SET_REPLY, sendbuf);
    Serial.println(sendbuf);
    client.publish(ONENET_TOPIC_PROP_SET_REPLY, sendbuf);
  }
  if (strstr(topic, ONENET_TOPIC_PROP_GET))
  {
    DeserializationError error = deserializeJson(doc, payload);
    if (error)
    {
      Serial.println("parse json failed");
      return;
    }
    JsonObject setAlinkMsgObj = doc.as<JsonObject>();
    serializeJsonPretty(setAlinkMsgObj, Serial);
    String str = setAlinkMsgObj["id"];
    Serial.println(str);
    char sendbuf[120];
    sprintf(sendbuf, "{\"id\":\"%s\",\"code\":0,\"msg\":\"succ\",\"data\":{\"ti_wen\":%.1f,\"heart\":%d,\"shuai_dao\":%d}}", str.c_str(), ti_wen, heart, shuai_dao);
    Serial.println(sendbuf);
    client.publish(ONENET_TOPIC_PROP_GET_REPLY, sendbuf);
    Serial.println(sendbuf);
    
  }
  
  // if (strstr(topic, ONENET_TOPIC_PROP_GET_SUB))
  // {

  //   DeserializationError error = deserializeJson(doc, payload);
  //   if (error)
  //   {
  //     Serial.println("parse json failed");
  //     return;
  //   }
  //   JsonObject setAlinkMsgObj = doc.as<JsonObject>();
  //   serializeJsonPretty(setAlinkMsgObj, Serial);
  //   String str = setAlinkMsgObj["id"];
  //   Serial.println(str);
  //   char sendbuf[100];
  //   //sprintf(sendbuf, "{\"id\": \"%s\",\"code\":200,\"msg\":\"success\",\"data\":{\"switch_light_colortemp\":%d,\"Body_temperature\":%d}}", str.c_str(), switch_light_colortemp, Body_temperature);
  //   sprintf(sendbuf, "{\"id\": \"%s\",\"code\":200,\"msg\":\"succ\",\"data\":{\"heart\":%d ,\"ti_wen\":%.2f,\"shuai_dao\":\"%s\"}}", str.c_str(), heart, ti_wen, shuai_dao);
  //   Serial.println(sendbuf);
  //   client.publish(ONENET_TOPIC_PROP_GET_REPLY, sendbuf);
  //   Serial.println(sendbuf);
  //   client.publish(ONENET_TOPIC_PROP_GET_REPLY, sendbuf);
  // }
}

//初始化订阅主题
void init_subscribe_topic()
{
  client.subscribe(ONENET_TOPIC_PROP_SET);
  client.subscribe(ONENET_TOPIC_PROP_GET);
  client.subscribe(ONENET_TOPIC_PROP_POST);
  //订阅获取子设备

}

// void update_date(void)
// {
//   switch_light_colortemp++;
//   Body_temperature++;
//   switch_light_colortemp++;
//   }

 
 void sub_publishTopic(String function,JsonDocument doc) {
   String topicStr = Doc1[function].as<String>();
  //取出json串中properties键中heart，ti_wen,shuai_dao等键的值
  
  
  
  //同时发送数据到网关上
  char sendbuf[120];
  sprintf(sendbuf, "{\"id\":\"123\",\"version\":\"1.0\",\"params\":{\"ti_wen\":{\"value\":%.1f},\"heart\":{\"value\":%d},\"shuai_dao\":{\"value\":%d}}}", ti_wen, heart, shuai_dao);
  Serial.println(sendbuf);
  Serial.println("this is sendbuf");
  
  client.publish(ONENET_TOPIC_PROP_POST, sendbuf);
  
  // 将JsonDocument的内容序列化为字符串
  String payload;
  serializeJson(doc, payload);
  Serial.println("topic: " + topicStr);
  Serial.println("payload: " + payload);
  // 发布主题和序列化后的payload
  //如果发送成功
  if (client.publish(topicStr.c_str(), payload.c_str())) {
    Serial.println("Publish success");
  }
  else {
    Serial.println("Publish failed");
  }
}
//处理从串口接受到的数据;
void handleReceived(String json) {
    //检查json是否符合josn格式
    
    
    DeserializationError error = deserializeJson(doc2, json);
    //打印已经转换成功的josn数据
    //打印error
    Serial.println(error.c_str());
    if (!error) {
        // 处理JSON数据
        // 例如，获取"function"键的值
        // 检查“function”键是否存在
        if (doc2.containsKey("function")) {
        // 获取“function”键的值
        String function = doc2["function"];
        // 删除“function”键
        doc2.remove("function");
        //传递function给函数
        //执行到了处理数据
        String properties= doc2["properties"];

        sub_publishTopic(function,doc2);
        
        Serial.println(function);

        
      
        }
        //通过HTTP协议在网关设备下根据function值进行对应的主题发布
        else {
            // 处理没有"function"键的情况
            Serial.println("JSON数据中没有'function'键,数据不完整");

            return;
        }
    }
    else {
        // 处理JSON解析错误
        Serial.println("JSON解析错误");
        //打印出因为什么的原因解析出错
        
        return;
    }
}
//读取模拟串口的数据
void read_fack_data() {
  //模拟heart ti_wen shuai_dao的值
  heart = random(70, 80);
  ti_wen = random(20, 30);
  shuai_dao = random(0,2);
  //将数据转换为json格式
  String json = "{\"function\":\"post\",\"id\":\"123\",\"version\":\"1.0\",\"params\":[{\"identity\":{\"productID\":\"wQ8Pc8iCag\",\"deviceName\":\"lora_child_1\"},\"properties\":{\"ti_wen\":{\"value\":" + String(ti_wen, 2) + "},\"heart\":{\"value\":" + String(heart) + "},\"shuai_dao\":{\"value\":" + String(shuai_dao) + "}}}] }";
  Serial.println(json);
  handleReceived(json);
  delay(1000);
}



//读取串口所传来的json信息
void readSerialMessage() {
  while (Serial.available() > 0) {
    String message = Serial.readStringUntil('\n');
    //连接两个字符串
    // String message2=message+message1;
    
    //Serial.println(message);
    //执行到了读取数据
    Serial.println("received");
    //处理接收到的json数据
    Serial.println(message);
    handleReceived(message);

}
  return ;
}
void init_display()
{
  // 初始化 I2C 总线，指定 SDA 和 SCL 引脚
  Wire.begin(4, 5);

  // 初始化 OLED 显示屏
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  }

  // 清空显示屏缓冲区
  display.clearDisplay();
}
void setup() {
  // put your setup code here, to run once:
  init_display();
  
  init_publishTopic();
  Serial.begin(115200); //初始化串口
  //Wire.begin();
  //sht20.begin();
  delay(3000);
  showWifiConected();
  setup_wifi();                                           //调用函数连接WIFI
  client.setServer(mqtt_server, port);                   //设置客户端连接的服务器,连接Onenet服务器, 使用1883端口
  delay(2000);
  Serial.println("setServer Init!"); 
  //client.setCallback(callback);
  client.connect(TM_DEVICE_NAME, mqtt_user, mqtt_password); //客户端连接到指定的产品的指定设备.同时输入鉴权信息
  delay(2000);
  Serial.println("connect Init!"); 
  
  if (client.connected())
  {
    Serial.println("OneNet is connected!"); //判断以下是不是连好了.
  }
  client.subscribe(ONENET_TOPIC_PROP_SET);
  client.subscribe(ONENET_TOPIC_PROP_GET);
  client.setCallback(callback);
  //加载等待动画
  showStartupAnimation();
  // tim1.attach(8, sendswitch_light_colortempAndBody_temperature); //定时每20秒调用一次发送数据函数sendswitch_light_colortempAndBody_temperature
  // // tim2.attach(4, update_date);
  // switch_light_colortemp =10;
  // Body_temperature =20;
  // switch_light_colortemp = 100;
}

int count = 0; //记录发送次数

void loop() {
  // put your main code here, to run repeatedly:

  if (!WiFi.isConnected()) //先看WIFI是否还在连接
  {
    setup_wifi();
  }
  if (!client.connected()) //如果客户端没连接ONENET, 重新连接
  {
    reconnect();
    delay(100);
  }
  client.loop(); //客户端循环检测
  read_fack_data();

 
 
}


